﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Data.Objects;
using System.Data.Objects.DataClasses;
using System.Linq;
using System.Linq.Expressions;
using Super.Common.Reflection;
using Super.Entities;

namespace Super.Infrastructure
{
    /// <summary>
    /// 很犀利的仓储模型
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class Repository<T> where T : EntityObject, new()
    {
        private readonly IObjectSet<T> _objectSet;
        private readonly ObjectContext _context;
        private readonly IObjectSetFactory _objectSetFactory;
        public Repository(IObjectSetFactory adp)
        {
            _objectSet = adp.CreateObjectSet<T>();
            _context = adp.CreateObjectContext();
            _objectSetFactory = adp;
        }

        #region IRepository<T> Members

        public IQueryable<T> AsQueryable()
        {
            return _objectSet;
        }

        public IEnumerable<T> GetAll(params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = AsQueryable();
            query = PerformInclusions(includeProperties, query);
            return query.ToList();
        }

        public IEnumerable<T> Find(Expression<Func<T, bool>> where,
                                   params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = AsQueryable();
            query = PerformInclusions(includeProperties, query);
            var sql = query.Where(where).ToString();
            return query.Where(where);
        }

        public T Single(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = AsQueryable();
            query = PerformInclusions(includeProperties, query);

            return query.Single(where);
        }

        public T First(Expression<Func<T, bool>> where, params Expression<Func<T, object>>[] includeProperties)
        {
            IQueryable<T> query = AsQueryable();
            query = PerformInclusions(includeProperties, query);
            return query.First(where);
        }

        public void Delete(T entity, bool isTrueDelete = false)
        {
            if (isTrueDelete)
            {
                _objectSet.DeleteObject(entity);
            }
            else
            {
                Type type = typeof(T);
                var pro = type.GetProperty("IsDel");
                DynamicMethodCompiler.CreateSetPropertyHandler<T>(pro).Invoke(entity, true);
                _objectSetFactory.ChangeObjectState(entity, EntityState.Modified);
            }
        }

        public void Insert(T entity)
        {

            _objectSet.AddObject(entity);
        }

        /// <summary>
        /// 先get之后再更新
        /// </summary>
        /// <param name="entity"></param>
        public void Update(T entity)
        {
            _objectSetFactory.ChangeObjectState(entity, EntityState.Modified);
        }

        /// <summary>
        /// 插入或者更新
        /// </summary>
        /// <param name="entity"></param>
        public void InsertOrUpdate(T entity)
        {
            Type type = typeof(T);
            var pro = type.GetProperty("ID");
            //用到了快速反射效率不是很高
            var id = DynamicMethodCompiler.CreatePropertyGetHandler<T>(pro).Invoke(entity);

            if ((int)id > 0)
            {
                if (entity.EntityKey == null)
                {
                    _objectSet.Attach(entity);
                }
                _objectSetFactory.ChangeObjectState(entity, EntityState.Modified);
            }
            else
            {
                _objectSet.AddObject(entity);
            }

        }

     
        #endregion

        #region private

        private static IQueryable<T> PerformInclusions(IEnumerable<Expression<Func<T, object>>> includeProperties, IQueryable<T> query)
        {
            return includeProperties.Aggregate(query, (current, includeProperty) => current.Include(includeProperty));
        }

        #endregion
    }
}